package play.go.model;

import java.io.Serializable;

import org.slim3.datastore.Attribute;
import org.slim3.datastore.Model;

import play.go.rule.Board;
import play.go.rule.Color;
import play.go.rule.SmartGameFormat;

import com.google.appengine.api.datastore.Key;

@Model(schemaVersion = 1)
public class Game implements Serializable {

    private static final long serialVersionUID = 1L;

    @Attribute(primaryKey = true)
    private Key key;
    @Attribute(unindexed = true)
    private Key black;
    @Attribute(unindexed = true)
    private Key white;
    @Attribute(unindexed = true)
    private Key player;
    @Attribute(unindexed = true)
    private Key winner;
    @Attribute(unindexed = true)
    private String ab;
    @Attribute(unindexed = true)
    private String aw;
    @Attribute(lob = true)
    private String record;
    @Attribute(unindexed = true)
    private int turn;
    @Attribute(unindexed = true)
    private int blackHama;
    @Attribute(unindexed = true)
    private int whiteHama;
    @Attribute(lob = true)
    private int[][] grid;

    public Game() {
    }

    /**
     * @return the key
     */
    public Key getKey() {
        return key;
    }

    /**
     * @param key
     *            the key to set
     */
    public void setKey(Key key) {
        this.key = key;
    }

    /**
     * @return the black
     */
    public Key getBlack() {
        return black;
    }

    /**
     * @param black
     *            the black to set
     */
    public void setBlack(Key black) {
        this.black = black;
    }

    /**
     * @return the white
     */
    public Key getWhite() {
        return white;
    }

    /**
     * @param white
     *            the white to set
     */
    public void setWhite(Key white) {
        this.white = white;
    }

    /**
     * @return the player
     */
    public Key getPlayer() {
        return player;
    }

    /**
     * @param player
     *            the player to set
     */
    public void setPlayer(Key player) {
        this.player = player;
    }

    /**
     * @return the ab
     */
    public String getAb() {
        return ab;
    }

    /**
     * @param ab
     *            the ab to set
     */
    public void setAb(String ab) {
        this.ab = ab;
    }

    /**
     * @return the aw
     */
    public String getAw() {
        return aw;
    }

    /**
     * @param aw
     *            the aw to set
     */
    public void setAw(String aw) {
        this.aw = aw;
    }

    /**
     * @return the record
     */
    public String getRecord() {
        return record;
    }

    /**
     * @param record
     *            the record to set
     */
    public void setRecord(String record) {
        this.record = record;
    }

    public void appendRecord(String rec) {
        setRecord(this.record + rec);
    }

    /**
     * @return the turn
     */
    public int getTurn() {
        return turn;
    }

    /**
     * @param turn
     *            the turn to set
     */
    public void setTurn(int turn) {
        this.turn = turn;
    }

    /**
     * @return the grid
     */
    public int[][] getGrid() {
        return grid;
    }

    /**
     * @param grid
     *            the grid to set
     */
    public void setGrid(int[][] grid) {
        this.grid = grid;
    }

    /**
     * @return the winner
     */
    public Key getWinner() {
        return winner;
    }

    /**
     * @param winner
     *            the winner to set
     */
    public void setWinner(Key winner) {
        this.winner = winner;
    }

    /**
     * @return the blackHama
     */
    public int getBlackHama() {
        return blackHama;
    }

    /**
     * @param blackHama the blackHama to set
     */
    public void setBlackHama(int blackHama) {
        this.blackHama = blackHama;
    }

    /**
     * @return the whiteHama
     */
    public int getWhiteHama() {
        return whiteHama;
    }

    /**
     * @param whiteHama the whiteHama to set
     */
    public void setWhiteHama(int whiteHama) {
        this.whiteHama = whiteHama;
    }

    public void nextTurn() {
        this.turn++;
        if (isBlack(this.player)) {
            setPlayer(white);
        } else {
            setPlayer(black);
        }
        System.out.println(this.record);
    }

    public void play(Player player, int x, int y) {
        boolean isBlack = isBlack(player);
        int color = isBlack ? Color.BLACK : Color.WHITE;
        Board grids = new Board(this.grid);
        int hama = grids.play(color, x, y);
        if (isBlack) {
            setBlackHama(blackHama + hama);
        } else {
            setWhiteHama(whiteHama + hama);
        }
        appendRecord(SmartGameFormat.format(color, x, y));
        nextTurn();
    }

    public boolean isBlack(Key player) {
        return player.equals(black);
    }

    public boolean isBlack(Player player) {
        return isBlack(player.getKey());
    }

    public boolean isWhite(Key player) {
        return !isBlack(player);
    }

    public boolean isWhite(Player player) {
        return !isBlack(player);
    }

    public void resign(Player player) {
        setPlayer(null);
        if (player.getKey().equals(black)) {
            setWinner(white);
        } else {
            setWinner(black);
        }
    }

    public int[] getBlackBefore() {
        int[] b = getBefore(record.lastIndexOf('B'));
        if (b == null) return b;
        if (this.grid[b[2]][b[1]] == 0) return null;
        if (this.player.equals(white)) {
            b[0] = this.turn;
        } else {
            b[0] = this.turn - 1;
        }
        return b;
    }

    public int[] getWhiteBefore() {
        int[] b = getBefore(record.lastIndexOf('W'));
        if (b == null) return b;
        if (this.grid[b[2]][b[1]] == 0) return null;
        if (this.player.equals(black)) {
            b[0] = this.turn;
        } else {
            b[0] = this.turn - 1;
        }
        return b;
    }

    int[] getBefore(int idx) {
        if (idx == -1) return null;
        if (this.player == null) return null;
        // ;B[aa]
        return SmartGameFormat.parse(record.substring(idx, idx + 4));
    }
}
